#!/usr/bin/env bash
#
# downXXX.sh - Programa para efetuar downloads de fotos e vídeos de sites XXX
#
# ultima modificacao:
# Site              : http://code.google.com/p/downxxx/
# Autor             : Roger Pereira Boff <rogerboff@gmail.com>
# Versão            : 0.0.3
# Licença           : GPLv3
# Codificação       : UTF8
#
#  -------------------------------------------------------------------------------------------------
#   Este programa permite baixar os conteudos de sites que hospedam materia adulto de uma forma rá-
#   pida e simples, separando o conteudo conforme o tipo e o site.
#
#   Exemplos:
#      $ ./downXXX.sh
#      Informe o link: http://wwww.xxx.xxx/xxxx
#      $
#
#  -------------------------------------------------------------------------------------------------
#
#
# Histórico:
#
#
#
#
#  -------------------------------------------------------------------------------------------------
# Variáveis Globais
# Define o nome do programa conforme o nome do arquivo.
PROGRAMA="$(basename "${0%.sh}")"

# Diretório onde serão salvos os arquivos que forem efetuaos os downloads.
DIRETORIO="${HOME}/downxxx"

# Diretório onde serão salvos as imagens.
FOTOSDIR="${DIRETORIO}/fotos"

# Diretório onde serão salvos os vídeos.
VIDEOSDIR="${DIRETORIO}/videos"

# Arquivo de log do programa.
LOGFILE="${DIRETORIO}/${PROGRAMA}.log"

# Diretório onde será gravado os logs. É necessário criar essa variável, pois pode ser mudado
# o local onde será armazenado os logs e será necessário saber se é possível gravar no diretório.
DIRLOGFILE="$(dirname "${LOGFILE}")"

# Habilita o modo de debug do programa.
DEBUGTRACE=1

# Define a quantidade de linhas do código a serem mostrados quando invocado a função geraBackTrace
LINHASTRACE=3

# Habilita/Desabilita o programa para usar notify-send para gerar avisos na tela.
AVISOPOPUP="1"

# Nível de log a ser gerado pela aplicação.
# Debug 		=  2
# Informativo	=  1
# Normal		=  0
# Critico		= -1
# Fatal			= -2
NIVELLOG="1"

#  -------------------------------------------------------------------------------------------------
# Function log
# Gerar arquivo de log das mensagens da aplicação e dispara notificação no desktop quando habilitado.
function log() {

	# Proteção contra loop na função, pois a função fatal() chama a função log() para gravação de
	# logs e é utilizado na função log() para tratar exceção na gravação de logs da aplicação.
    [ -z "${fatalLog}" ] || return 0
    
    # Caso não esteja habilitado o nível de debug, será setado o nível normal(0).
    [ -n "${NIVELLOG}" ] || NIVELLOG=0
    
    # Tipo da mensagem de log
    local tipo

    # Mensagem que será gravado no log.
    local mensagem
    
    # Função que chamou a função log.
    local funcao="${FUNCNAME[1]}"
    
    [ "${funcao}" == "fatal" ] && funcao="${FUNCNAME[2]}"
    
    # Nível de mensagem de log a ser gravado.
    local nivel

    # Data e hora que a mensagem foi disparada.
    local dataHora="$(date "+%Y-%m-%d %R")"
    
    if [ "$#" -le "1" ]
    then
        fatal "Faltam ou não foram informados os parâmetro a função."

    elif [ "$#" -eq "2" ]
    then
        tipo="${1}"
        mensagem="${2}"
        
	elif [ "$#" -eq "3" ]
	then
		tipo="${1}"
		mensagem="${2}"
		notDesktTipo="${3}"
		
		# Dispara uma notificação no desktop 
		notDesk "${notDesktTipo}" "${mensagem}"
		
    else
        fatal "Excesso de parâmetros para a função."
    fi
    
	# Nível de logs aceitos para a função.		        
    case $tipo in
    	Debug)
        	nivel="2"
       	;;
        Informativo)
        	nivel="1"
        ;;
        Normal)
        	nivel="0"
        ;;
        Critico)
       		nivel="-1"
       	;;
       	Fatal)
        	nivel="-2"
        ;;
        *)
        	fatal "Nível de log não configurado."
      	;;
    esac
    
	# Verifica se o nível de log selecionado é compatível com o solicitado para impressão
	[ "${nivel}" -le "${NIVELLOG}" ] || return 0
    
    # Verifica se é possível gravar o log no diretório e no arquivo de log.
    if [ -f "${LOGFILE}" -a -w "${LOGFILE}" -o -d "${DIRETORIO}" -a -w "${DIRLOGFILE}"  ]
    then
    
    	# Gravação de log de nível Debug para a função log().
    	#if [ "${NIVELLOG}" -eq "2" ]
    	#then
    	#	echo -e "[${dataHora} - Pid: $$ - Debug] log(): Array(${#FUNCNAME[*]}) [${FUNCNAME[*]}] - Tipo: ${tipo} - Mensagem \"${mensagem}\"" >> "${LOGFILE}"
    	#fi
    
       	echo -e "[${dataHora} - Pid: $$ - ${tipo}] ${funcao}(): ${mensagem}" >> "${LOGFILE}"
        
    else
        # Ativa a proteção contra loop infinito.
        fatalLog="true"

        fatal "Não foi possível gerar o arquivo de log. Verifique se você tem permissão para gerar o log em \"${LOGFILE}\""
   fi
}
# Function Fatal
# Mostra a mensagem de erro fatal, gera backtrace (Caso esteja habilitada a var $DEBUGTRACE) e fina-
# liza o programa.
function fatal() {

    local mensagem="$*" # Mensagem a ser mostrada.

    # Variável contendo o nome da função que disparou o erro fatal da aplicação.
    local funcao="${FUNCNAME[$((${#FUNCNAME[*]}-1))]}"

    echo -e "Erro Fatal: Função ${funcao}: ${mensagem}\n"

    # Grava o erro no log.
    #log "Fatal" "Erro Fatal: Função ${funcao}: ${mensagem}"
    log "Fatal" "Erro Fatal: Função ${funcao}: ${mensagem}"

    # Chama a função responsável pela geração do backtrace.
    geraBackTrace

    exit 1

}

# Gerar um (pseudo) backtrace da aplicação para analise de erros.
# A geração do backtrace não é automático, sendo necessário completar a analise com o arquivo trace
# gerado pela funcao "debugPrograma()" ou "fatal()"
function geraBackTrace(){

    # Caso a variável $DEBUGTRACE não esteja habilitado, retorna para a função.
    [ "${DEBUGTRACE}" -eq "1"  ] || return 0

	# Desabilita a impressão de código e o debug da execução da aplicação.
    # Desabilite a função abaixo para gerar um debug dessa função.
    set +xv

	log "Debug" "Gerando o back trace da aplicação."
		
    echo -e "Gerando back trace da aplicação."

	log "Debug" "Funções atuais estanciadas: Array(${#FUNCNAME[*]}) [${FUNCNAME[*]}]"
	
    local t="$((${#FUNCNAME[*]}-2))" # Total de registros na array.
    local i # Incremento dos arrays.
    local lI # Linha inicial do código a ser lido.
    local lF # Linha final do código a ser lido.

    # As duas variáveis abaixo são utilizadas somente para dar um destaque especial para as linhas
    # que efetuaram a chamada da função que apresentou problema.
    local smso="$(tput smso)" # Habilita o terminal para destacar a linha.
    local rmso="$(tput rmso)" # Desabilitado o terminal para não destacar mais a linha.

    local arquivoBackTrace="${HOME}/${PROGRAMA}.backtrace.log"
    
    # Caso o arquivo da variável "$arquivoBackTrace" exista, será apagado.
    [ ! -f "${arquivoBackTrace}" ] || rm "${arquivoBackTrace}" 

    # FIXME: Não sei exatamente por que o sed apresenta erro ao utilizar ${BASH_LINENO[${i}]}, pois
    # parece que não está conseguindo expandir a variável, o qual foi necessário contornar com a
    # variável "$linha". Mistérios da meia-noite.

    # Número da linha executada.
    local linha

    # Incremento da função em relação as outras arrays. Isso é necessário, pois ${FUNCNAME[x]}
    # retorna o nome da função em execução e não da função que chamou a função, o que implica em
    # avançar o array em relação as outras arrays para conseguir a pegar o nome da função correta.
    local f="2"

    # Definições das variáveis utilidas:
    # $BASH_LINENO - Retorna uma array com as linhas executadas até o backtrace ser gerado.
    # $BASH_SOURCE - Retorna uma array com o arquivo responsável pelo códigos executados.
    # $FUNCNAME - Retorna uma array com as funções invocadas na aplicação. A função "main" é o códi-
    # go principal da aplicação.

	log "Debug" "Linhas: Array(${#BASH_LINENO[*]}) [${BASH_LINENO[*]}] - Funções: Array(${#FUNCNAME[*]}) [${FUNCNAME[*]}]"

    # Laço responsável por gerar o backtrace da aplicação.
    for ((i=1;i<=t;i++))
    do
    
    	log "Debug" "Var \$i: ${i} - Var \$t: ${t} - Var \$f: ${f}"
    	
    	# Imprime o stack, linha, função (Utiliza a função posterior da array) e o arquivo com o có-
        # digo responsável pela chamada.
        echo -n "#:$((${i}-1)) - Linha: ${BASH_LINENO[${i}]}" | tee -a "${arquivoBackTrace}"
        echo -n " - Função: ${FUNCNAME[$f]}()" | tee -a "${arquivoBackTrace}"
        echo    " - Source: ${BASH_SOURCE[${i}]}:" | tee -a "${arquivoBackTrace}"
        
        log "Debug" "#:\${i}: #:${i} - \${BASH_LINENO[\${i}]}: ${BASH_LINENO[${i}]} - \${FUNCNAME[\$f]}(): ${FUNCNAME[$f]}() - \${BASH_SOURCE[\${i}]}: ${BASH_SOURCE[${i}]}"

        f=$((f+1))

        linha="${BASH_LINENO[${i}]}" # Seta o número da linha da execução.

        # Verifica se o número da linha é válido.
        if [ ${linha} -ne 0 ]
        then

        lI="$(($linha-${LINHASTRACE}))" # Pega X linhas iniciais definidos em $LINHASTRACE.
        lF="$(($linha+${LINHASTRACE}))" # Pega X linhas abaixo definidos em $LINHASTRACE.

        # Passos utilizado para coletar as linhas do código para o backtrace.
        # 1 - "cat" numera as linhas do arquivo para facilitar a leitura
        # 2 - "sed" coleta somente x (Quantidade de linhas depende da variável $LINHASTRACE) li-
        #     nhas acima e abaixo da linha que executou a função.
        # 3 - "sed" remove os tabs e espaços adiconados ao código pelo "cat".
        # 4 - "tee" copia a saída STDIN para o arquivo e tela.
        # 5 - "sed" efetua o destaque da linha que executou a função. (Utilizado o sed dessa ma-
        #     neira para não suja o código gravado em arquivo com caracteres de controle).
        cat -n "${BASH_SOURCE[${i}]}" | \
            sed -n "${lI},${lF}p" | \
            sed "
                s/^[ \t]\{1,\}/  /
                s/[\t]/: /
                " | \
            tee -a "${arquivoBackTrace}" | \
            sed "s/^[ ]\{1,\}${linha}.*/${smso}&  ${rmso}/"

        fi

        echo ""

    done

    echo -e "\n\n# XXX: Xtrace do aplicativo" >> "${arquivoBackTrace}"

    # Cópia o Xtrace (Habilitado pelo "set -xv") da aplicação para o arquivo de backtrace.
    cat "${arquivoTrace}" >> "${arquivoBackTrace}"

    echo "Arquivo back trace criado em: ${arquivoBackTrace}"

    exit 1

}

# Gera um trace de execução do programa para analise.
# XXX: Caso seja necessário gravar alguma informação adicional no trace, basta utilizar a saída
#      STDERR para jogar a informação para o arquivo de trace.
function debugPrograma() {

    [ "${DEBUGTRACE}" == "1"  ] || return 0

    # Nome do arquivo trace da aplicação que foi gerado da estancia atual.
    # TODO: Essa variável é definida em nível global, mas não sei se não seria melhor definir ela
    # no inicio da aplicação ou deixo como está, pois esse arquivo trace deve ser gravado dentro
    # da pasta quando gerado. Talvez seja melhor criar um parâmetro na função para retornar o
    # nome do arquivo Trace.
    arquivoTrace="/tmp/trace.$(date +%Y%m%d.%H%M).log"

    # Direciona todas as saídas STDERR para o arquivo de trace.
    # Importante: Qualquer erro será gravado dentro do arquivo e não será mostrado no terminal.
    exec 2> "${arquivoTrace}"

    echo "Atenção: Gravando trace em ${arquivoTrace}"

    # Habilita o bash a mostrar o código e a execução, o qual utiliza a saída STDERR.
    set -xv

}

# notDesk() Mostra uma notificação no desktop utilizando notify-send.
function notDesk() {

	log "Debug" "Preparando para gerar notificação no desktop."

    local tipo="${1}" # Tipo da notificação a ser gerada.
    local mensagem="${2}" # Mensagem da notificação

    # Verifica se todos os parâmetros foram informados.
    if [ -n "${tipo}" -a -n "${mensagem}" ]
    then
    
    	log "Debug" "Gerando notificação com os dados: tipo: ${tipo} - mensagem: ${mensagem}"

        # Verifica se o programa tem acesso ao display para emitir a notificação
        # e se está habilitado para mostrar mensagens com notify-send.
        # Para desabilitar a notificação com notify-send, basta setar a variável
        # global AVISOPOPUP com "0"
        if [ -n "${DISPLAY}" -a "${AVISOPOPUP}" = "1" ]
        then

        case ${tipo} in
            erro) # Emite uma notificação de erro.
                notify-send -i error -u low -t 1 "${PROGRAMA}" "${mensagem}"
            ;;
            ok) # Emite uma notificação de execução com sucesso.
                notify-send -i emblem-default -u critical -t 1 "${PROGRAMA}" "${mensagem}"
            ;;
            aviso) # Emite uma notificação de aviso.
                notify-send -i dialog-warning -u low -t 1 "${PROGRAMA}" "${mensagem}"
            ;;
            *)
                fatal "Tipo de aviso não configurado. (Parâmetro: ${tipo})"
            ;;
        esac
        fi

        # Imprime somente a mensagem no terminal.
        echo "${mensagem}"

    else
        fatal "Falta(m) parâmentro(s) para a função."
    fi

}

# Determina o tipo de processamento a ser efetuado no link conforme o dominio e o path do link.
# TODO: Criar uma verificação de link para saber se o link já não foi baixado.
function processaLink() {

    local link="${1}" # Link a ser processado.
    
    log "Debug" "Link solicitado a ser processado \"${link}\""

    local dominio # Utilizado para determinar o dominio do link.

    local processaDominio # Contem o dominio a ser processado.

    # Dominio do link a ser processao, o qual será utilizado para determinar qual função a ser
    # utilizada.
    local dominioLink="$(echo "${link}" | sed 's/http:\/\///;s/\/.*//')"
    
    log "Debug" "Dominio do link \"${dominioLink}\""

    # Lista de dominios que são aceitos pelo programa.
    local listaDominios="youporn.com xvideos.com xhamster.com"

    log "Debug" "Verificando dominio do link \"${link}\""

    # Determina qual o dominio do link baseado na variável "$listaDominios"
    for dominio in ${listaDominios}
    do
    	log "Debug" "Verificando se \"${dominioLink}\" é compatível com \"${dominio}\""
        # Verifica se o link corresponde a link.
        processaDominio="$(echo "${link}" | grep "${dominio}")"

        # Caso o dominio corresponda com o link, define o dominio a ser processado e para o loop.
        if [ -n "${processaDominio}" ]
        then
            processaDominio="${dominio}"
            
            log "Debug" "Dominio do link a ser processado \"${processaDominio}\""
            
            break
        fi

    done

    if [ -n "${processaDominio}" ]
    then
    	log "Normal" "Iniciando o processamento do link \"${link}\""
        echo "${processaDominio}"
    else
        log "Normal" "O link não pode ser processado." "aviso"
    fi

}

# Processa uma lista de links os quais devem ser efetuado os downloads.
# A lista deve ser um arquivo texto contendo somente os links sem nenhum tipo de codificação.
function processaListaLinks() {

    local lista="${1}" # Lista contendo os links a serem efetuado os downloads.
    
    log "Informativo" "Lista informado para ser processada \"${lista}\""

    if [ -f "${lista}" ]
    then
    	log "Normal" "Iniciando o processamento da lista." "ok"
    	
        # Processa a lista de link a serem baixado.
        while read linha
        do
			log "Debug" "Link a ser processado \"${linha}\""
			
            processaLink "${linha}"

        done < "${lista}"
        
        log "Normal" "Finalizado o processamento da lista" "ok"

    else
    	log "Normal" " Não foi encontrado a lista de links" "error"
    	
    fi

}

#
function principal() {

    case $1 in
        lista) # Iniciar o processamento de uma lista de links.

            # Somente para deixar mais claro para os que iniciando no mundo shell script, o 
            # comando "shift" avança a posição das variáveis conforme o valor informado.
            # $ man bash-builtins
            shift

            log "Normal" "Iniciando processamento da lista \"${1}\""

            # Chama a função responsável pelo processamento da lista.
            processaListaLinks "$1"

            # TODO: No momento, após processar a lista, o programa está fechando, mas deve ser re-
            #       visto esse comportamento, pois pode haver mais opções solicitadas.
            return 0
        ;;
        *)
            fatal "Opção não programada. Opção solicitada ($1)"
        ;;
    esac

}
#  -------------------------------------------------------------------------------------------------
# TODO: Retirar esse comando após finalizar o programa.
clear
#  -------------------------------------------------------------------------------------------------
debugPrograma
log "Normal" "Iniciando a aplicação"
principal "lista" "/home/alemaum/Documentos/lista.xhamster"
log "Normal" "Finalizado a aplicação com sucesso."
